"""
工具函数模块
"""
import pandas as pd
import numpy as np
import datetime


def date_to_str(date, format='%Y-%m-%d'):
    """
    将日期转换为字符串
    
    Parameters
    ----------
    date : datetime.datetime or datetime.date
        日期对象
    format : str, optional
        日期格式，默认为'%Y-%m-%d'
        
    Returns
    -------
    str
        日期字符串
    """
    if date is None:
        return None
    return date.strftime(format)


def str_to_date(date_str, format='%Y-%m-%d'):
    """
    将字符串转换为日期
    
    Parameters
    ----------
    date_str : str
        日期字符串
    format : str, optional
        日期格式，默认为'%Y-%m-%d'
        
    Returns
    -------
    datetime.datetime
        日期对象
    """
    if date_str is None:
        return None
    return datetime.datetime.strptime(date_str, format)


def resample_ohlc(df, freq, datetime_column=None):
    """
    重采样OHLC数据
    
    Parameters
    ----------
    df : pandas.DataFrame
        包含OHLC数据的DataFrame
    freq : str
        重采样频率，如'D'(日)、'W'(周)、'M'(月)等
    datetime_column : str, optional
        日期时间列的名称，如果为None则使用索引
        
    Returns
    -------
    pandas.DataFrame
        重采样后的DataFrame
    """
    if datetime_column is not None:
        if datetime_column in df.columns:
            df = df.set_index(datetime_column)
    
    # 确保索引是日期时间类型
    if not isinstance(df.index, pd.DatetimeIndex):
        raise ValueError("索引必须是日期时间类型")
    
    # 重采样
    resampled = df.resample(freq)
    
    # 聚合OHLC数据
    result = pd.DataFrame({
        'open': resampled['open'].first(),
        'high': resampled['high'].max(),
        'low': resampled['low'].min(),
        'close': resampled['close'].last()
    })
    
    # 如果有成交量列，也进行重采样
    if 'volume' in df.columns:
        result['volume'] = resampled['volume'].sum()
    
    return result


def calculate_returns(prices, method='simple'):
    """
    计算收益率
    
    Parameters
    ----------
    prices : pandas.Series
        价格序列
    method : str, optional
        计算方法，'simple'(简单收益率)或'log'(对数收益率)，默认为'simple'
        
    Returns
    -------
    pandas.Series
        收益率序列
    """
    if method == 'simple':
        returns = prices.pct_change()
    elif method == 'log':
        returns = np.log(prices / prices.shift(1))
    else:
        raise ValueError("method必须是'simple'或'log'")
    
    return returns.fillna(0)


def calculate_drawdowns(returns):
    """
    计算回撤
    
    Parameters
    ----------
    returns : pandas.Series
        收益率序列
        
    Returns
    -------
    pandas.Series
        回撤序列
    """
    # 计算累计收益
    cumulative = (1 + returns).cumprod()
    
    # 计算历史最高点
    running_max = cumulative.cummax()
    
    # 计算回撤
    drawdowns = (cumulative / running_max) - 1
    
    return drawdowns


def calculate_sharpe_ratio(returns, risk_free_rate=0, periods_per_year=252):
    """
    计算夏普比率
    
    Parameters
    ----------
    returns : pandas.Series
        收益率序列
    risk_free_rate : float, optional
        无风险利率，默认为0
    periods_per_year : int, optional
        一年的周期数，默认为252（交易日）
        
    Returns
    -------
    float
        夏普比率
    """
    # 计算超额收益
    excess_returns = returns - risk_free_rate / periods_per_year
    
    # 计算夏普比率
    sharpe_ratio = np.sqrt(periods_per_year) * excess_returns.mean() / excess_returns.std()
    
    return sharpe_ratio


def calculate_sortino_ratio(returns, risk_free_rate=0, periods_per_year=252, target_return=0):
    """
    计算索提诺比率
    
    Parameters
    ----------
    returns : pandas.Series
        收益率序列
    risk_free_rate : float, optional
        无风险利率，默认为0
    periods_per_year : int, optional
        一年的周期数，默认为252（交易日）
    target_return : float, optional
        目标收益率，默认为0
        
    Returns
    -------
    float
        索提诺比率
    """
    # 计算超额收益
    excess_returns = returns - risk_free_rate / periods_per_year
    
    # 计算下行波动率
    downside_returns = excess_returns[excess_returns < target_return]
    if len(downside_returns) == 0 or downside_returns.std() == 0:
        return float('inf')
    
    # 计算索提诺比率
    sortino_ratio = np.sqrt(periods_per_year) * excess_returns.mean() / downside_returns.std()
    
    return sortino_ratio
